home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / StereoScope-3.2 / StereoView.m < prev    next >
Encoding:
Text File  |  1992-08-12  |  17.4 KB  |  735 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. #import "StereoView.h"
  5. #import "CubeView.h"
  6. #import "Help.h"
  7. #import <appkit/Control.h>
  8. #import    <appkit/Button.h>
  9. #import <appkit/graphics.h>
  10. #import <appkit/OpenPanel.h>
  11. #import <NXCType.h> 
  12. #import <stdlib.h>
  13. #import <math.h>
  14. #import <strings.h>
  15. #import <appkit/Pasteboard.h>
  16. #import <dpsclient/dpsNeXT.h>
  17. #import <defaults/defaults.h>      // for NXArgv
  18. #import <libc.h>        // for chdir, getwd
  19. #define EYE 2.9/57.3
  20. #define rad_to_deg  (180./M_PI)
  21. #define MAXLINE 10000
  22. #define MAXNUM  10000
  23. #define MAXALLOC 100
  24. #define TRUE -1
  25. #define FALSE !TRUE
  26.  
  27. @implementation StereoView
  28.  
  29. - initFrame:(const NXRect *)frameRect
  30. {
  31.     
  32.     textDidChange = 0;
  33.     textNeedsSaving = 0;
  34.   toshow = (datapoints **) calloc(MAXALLOC, sizeof(datapoints *));
  35.   max_path = 100;
  36.   path = (float *) calloc(2*max_path, sizeof(float));
  37.     eyeOffset = EYE;
  38.     openReq = [OpenPanel new];
  39.     saveReq = [SavePanel new];
  40.     [super initFrame:frameRect];
  41.     bcopy(frameRect,&left_Rect,sizeof(NXRect));
  42.     bcopy(frameRect,&right_Rect,sizeof(NXRect));
  43.  
  44.     NXDivideRect(&left_Rect,&right_Rect,NX_WIDTH(frameRect)/2.,2);
  45.  
  46. /*
  47.     NXInsetRect(&left_Rect,0.1*NX_WIDTH(&left_Rect),0.1*NX_HEIGHT(&left_Rect));
  48.     NXInsetRect(&right_Rect,0.1*NX_WIDTH(&right_Rect),0.1*NX_HEIGHT(&right_Rect));
  49. */
  50.     leftView = [[CubeView alloc] initFrame:&left_Rect];
  51.     rightView = [[CubeView alloc] initFrame:&right_Rect];
  52.     [self setDrawOrigin:(NXCoord) NX_X(frameRect):(NXCoord) NX_Y(frameRect)];
  53.     [self addSubview:leftView];
  54.     [self addSubview:rightView];
  55.     [self setAutoresizeSubviews:TRUE];
  56.     fileNAME = (char *) calloc(1,256);
  57.     strcpy(fileNAME,"Untitled");
  58.     [self S_Reset:self];
  59.     return self;
  60. }
  61.  
  62. static char  angle_buffer[20];
  63.  
  64.  
  65. - setDelegate:anObject
  66. {
  67.     delegate = anObject;
  68.     return self;
  69. }
  70.  
  71. - copy:sender            /* code contributed by Dan McCreary of NeXT */
  72. {
  73.   id pb = [Pasteboard new];    /* global Pasteboard object */ 
  74.   NXStream  *st;        /* stream to collect data in */ 
  75.   char      *data;        /* actual data buffer */ 
  76.   int       length;        /* length of data */ 
  77.   int       maxLength;        /* (not used here) */
  78.                 /* To see how to use the pasteboard */
  79.                 /* see page 10-33 of the SysRefMan */
  80.   [self  prtCopy];
  81.                 /* declare that we will supply a */
  82.                 /* single type of data: PostScript */
  83.   [pb declareTypes:&NXPostScriptPboard num:1 owner:self];
  84.                 /* get a stream which writes to memory */ 
  85.   st = NXOpenMemory( NULL, 0, NX_WRITEONLY );
  86.                 /* write PostScript code for this view into the stream */ 
  87.   [self copyPSCodeInside:NULL to:st];
  88.                 /* get actual data buffer from stream */ 
  89.   NXGetMemoryBuffer( st, &data, &length, &maxLength );
  90.                 /* write PostScript data to pasteboard */
  91.   [pb writeType:NXPostScriptPboard data:data length:length];
  92.                 /* deallocate stream, including its buffer */ 
  93.   NXCloseMemory( st, NX_FREEBUFFER );
  94.   [self display];
  95.   return self; 
  96. }
  97.  
  98.  
  99. -(BOOL)acceptsFirstResponder
  100. {                /* make this view accept first */
  101.     return YES;        /* responder so it will understand copy */
  102. }
  103.  
  104. /*
  105. -resignFirstResponder
  106. {
  107.     return self;
  108. }
  109. */
  110. - showError:(char *)errorMessage
  111. {
  112.   NXRunAlertPanel("Error", errorMessage, "OK", NULL, NULL);
  113.   return self;
  114. }
  115.  
  116. - prtCopy
  117. {
  118.       [leftView printPSCode:leftView];
  119.       [rightView printPSCode:rightView];
  120.     [self display];
  121.     return self;
  122. }
  123.  
  124. - printPSCode:sender
  125. {
  126.   [leftView printPSCode:leftView];
  127.   [rightView printPSCode:rightView];
  128.   [self display];
  129.   [super printPSCode:self];
  130.   [self display];
  131.   return self;
  132. }
  133.  
  134. - textChanged:sender
  135. {
  136.     textDidChange = 1;
  137.     return self;
  138. }
  139.  
  140. - textEdited:sender
  141. {
  142.     textNeedsSaving = 1;
  143.     return self;
  144. }
  145.  
  146. - plot:sender
  147. {
  148.     NXStream    *stream;
  149.  
  150.     if (textDidChange == 1)    {
  151.     if (delegate && [delegate respondsTo:@selector(stereoView:providePoints:)])
  152.         [delegate stereoView:self providePoints:&stream];
  153.         textDidChange = 0;
  154.     [self readData:(NXStream *)stream];
  155.     [leftView getStruct:&toshow:max_path:&path];
  156.     [rightView getStruct:&toshow:max_path:&path];
  157.     [leftView reScale:self];
  158.     [rightView reScale:self];
  159.         }
  160.     [self set_Theta:self];
  161.     [self set_Phi:self];
  162.     [self set_dist:self];
  163.     [self display];
  164.     return self;
  165. }
  166.  
  167. - clear:sender
  168. {
  169.   long int count;
  170.   datapoints **all_data = toshow, *thesedata;
  171.  
  172.                 /* release existing data */
  173.   while ((thesedata = *all_data) != (datapoints *) NULL) {
  174.     free(thesedata->displayed);
  175.     count = 0;
  176.     while(thesedata->all[count] != (float *) NULL) {
  177.       free(thesedata->all[count]);
  178.       count++;
  179.     }
  180.     free(thesedata->all);
  181.     free(thesedata);
  182.     *all_data++ = (datapoints *) NULL;
  183.   }
  184.     [leftView getStruct:&toshow:max_path:&path];
  185.     [rightView getStruct:&toshow:max_path:&path];
  186.     [leftView clear:self];
  187.     [rightView clear:self];
  188.     [self set_Theta:self];
  189.     [self set_Phi:self];
  190.     [self set_dist:self];
  191.     [self display];
  192.     [self textChanged:self];
  193.     return self;
  194. }
  195.  
  196. - S_Reset:sender
  197. {
  198.     float    floatValue;
  199.     
  200.     [leftView getStruct:&toshow:max_path:&path];
  201.     [rightView getStruct:&toshow:max_path:&path];
  202.     [ThetaSlider setFloatValue:0.];
  203.     floatValue = [ThetaSlider floatValue];
  204.   sprintf(angle_buffer, "%6.2f", rad_to_deg*floatValue);
  205.   [showTheta setStringValue:angle_buffer];
  206.     [PhiSlider setFloatValue:0.];
  207.     floatValue = [PhiSlider floatValue];
  208.   sprintf(angle_buffer, "%6.2f", rad_to_deg*floatValue);
  209.   [showPhi setStringValue:angle_buffer];
  210.     [DistanceSlider setFloatValue:2.0];
  211.     floatValue = [DistanceSlider floatValue];
  212.   sprintf(angle_buffer, "%6.2f", floatValue);
  213.   [showDist setStringValue:angle_buffer];
  214.     [leftView Reset:leftView];
  215.     [rightView Reset:rightView];
  216.     [leftView setTheta:-eyeOffset];
  217.     [rightView setTheta:+eyeOffset];
  218.     [[self window] setTitle:fileNAME];
  219.     [self display];
  220.     return self;
  221. }
  222.  
  223. - set_Theta:sender
  224. {
  225.     float floatValue;
  226.     
  227.     floatValue = [ThetaSlider floatValue];
  228.   sprintf(angle_buffer, "%6.2f", rad_to_deg*floatValue);
  229.   [showTheta setStringValue:angle_buffer];
  230.   [leftView setTheta:floatValue-eyeOffset];
  231.   [rightView setTheta:floatValue+eyeOffset];
  232.   [self display];
  233.   return self;
  234. }
  235.  
  236. - set_Phi:sender
  237. {
  238.     float floatValue;
  239.     
  240.     floatValue = [PhiSlider floatValue];
  241.  
  242.   sprintf(angle_buffer, "%6.2f", rad_to_deg*floatValue);
  243.   [showPhi setStringValue:angle_buffer];
  244.   [leftView setPhi:floatValue];
  245.   [rightView setPhi:floatValue];
  246.   [self display];
  247.   return self;
  248. }
  249.  
  250.  
  251. - set_dist:sender
  252. {
  253.     float floatValue;
  254.     
  255.     floatValue = [DistanceSlider floatValue];
  256.  
  257.   sprintf(angle_buffer, "%6.2f", floatValue);
  258.   [showDist setStringValue:angle_buffer];
  259.   [leftView setdist:floatValue];
  260.   [rightView setdist:floatValue];
  261.   [self display];
  262.   return self;
  263. }
  264.  
  265. - setAxes:(int) intVal
  266. {    
  267.     [AxesButton setState:intVal];
  268.     [leftView setAxes:intVal];
  269.     [rightView setAxes:intVal];
  270.     [self display];
  271.     return self;
  272. }
  273.  
  274. - (int)getAxes
  275. {
  276.     return [AxesButton state];
  277. }
  278.  
  279. - toggle_Axes:sender
  280. {
  281.     [leftView toggleAxes:self];
  282.     [rightView toggleAxes:self];
  283.   [self display];
  284.   return self;
  285. }
  286.  
  287. - setCube:(int) intVal
  288. {
  289.     [CubeButton setState:intVal];
  290.     [leftView setCube:intVal];
  291.     [rightView setCube:intVal];
  292.     [self display];
  293.     return self;
  294. }
  295.  
  296. - (int)getCube
  297. {
  298.     return [CubeButton state];
  299. }
  300.  
  301. - toggle_Cube:sender
  302. {
  303.     [leftView toggleCube:self];
  304.     [rightView toggleCube:self];
  305.   [self display];
  306.   return self;
  307. }
  308.  
  309. - setEyeOffset:(float) floatVal
  310. {
  311.     int    eye;
  312.     
  313.     eyeOffset = floatVal;
  314.             
  315.     if (floatVal < 0.)eye = NO;
  316.         else eye = YES;
  317.     [EyeButton setState:eye];
  318.     [self set_Theta:self];
  319.     [self display];
  320.     return self;
  321. }
  322.  
  323. - (float) getEyeOffset
  324. {
  325.     return eyeOffset;
  326. }
  327.  
  328. - toggle_Eyes:sender
  329. {
  330.     [self setEyeOffset: - eyeOffset];
  331.   return self;
  332. }
  333.  
  334. - sizeTo:(NXCoord)width :(NXCoord)height
  335. {
  336.     NXRect frameRect;
  337.     
  338.     [super sizeTo:width :height];
  339.  
  340. /*
  341.     [leftView free];
  342.     [rightView free];
  343. */
  344.     [self getFrame:&frameRect];
  345.     bcopy(&frameRect,&left_Rect,sizeof(NXRect));
  346.     bcopy(&frameRect,&right_Rect,sizeof(NXRect));
  347.  
  348.     NXDivideRect(&left_Rect,&right_Rect,NX_WIDTH(&frameRect)/2.,2);
  349.  
  350. /*
  351.     leftView = [[CubeView alloc] initFrame:&left_Rect];
  352.     rightView = [[CubeView alloc] initFrame:&right_Rect];
  353. */
  354.     [leftView setFrame:&left_Rect];
  355.     [leftView initialize];
  356.     [rightView setFrame:&right_Rect];
  357.     [rightView initialize];
  358.     [self setDrawOrigin:(NXCoord) NX_X(&frameRect):(NXCoord) NX_Y(&frameRect)];
  359. /*
  360.     [self addSubview:leftView];
  361.     [self addSubview:rightView];
  362.     [self setAutoresizeSubviews:TRUE];
  363. */
  364.     [self S_Reset:self];
  365.     return self;
  366. }
  367.  
  368. - windowDidResize:sender
  369. {
  370.     /*    This is not received since the controller handles it */
  371.     printf("windowDidResize for StereoView\n");
  372.     return self;
  373. }
  374.  
  375. - windowWillResize:sender toSize:(NXSize *)aSize
  376. {
  377.     /*    This is not received since the controller handles it */
  378.     printf("windowWillResize for StereoView\n");
  379.     return self;
  380. }
  381.  
  382. - openData:sender
  383. {
  384.   const char *const ext[5] = {"dat", "data", "3d", "S3d", NULL};
  385.  
  386.     if (textNeedsSaving)
  387.         [self saveInRequest:sender];
  388.   if([openReq runModalForTypes:ext] && (fileNAME = (char *)[openReq filename]))    {
  389.     [self readSData:fileNAME];
  390.     }
  391.   else
  392.     [self showError:"No file chosen or could not open file"];
  393.   return self;
  394. }
  395.  
  396. // saveRequest: saves the current window under its default name (found in
  397. // the title bar). Note that if the title bar is empty or the default title
  398. // is "Untitled" then saveRequest: will put up a save panel, giving the user
  399. // a chance to specify a real title.
  400.  
  401. - saveRequest:sender
  402. {
  403.     const char *fileName;
  404.     const char *const types[2] = {"S3d",
  405.                       NULL};
  406.     
  407.     id curWin = [self window];
  408.     if (textNeedsSaving)
  409.         [self saveInRequest:sender];
  410.     [saveReq setRequiredFileType:types[0]];
  411.     
  412.     if (curWin == nil) 
  413.     [self showError:"errorOnSaveRequest"];
  414.     else {
  415.     // Check to see if the current window is titled and the title is not
  416.     // "Untitled". If so, save the file, else put up a save panel...
  417.     fileName = [curWin title];
  418.     if (strcmp (fileName, "Untitled"))
  419.         [self saveText:curWin inPath:fileName];
  420.     else [self saveInRequest:sender];
  421.     }
  422.     textNeedsSaving = 0;
  423.     return self;
  424. }
  425.  
  426. // saveInRequest: gives the user a chance to save the current window
  427. // under a new name. 
  428.  
  429. - saveInRequest:sender
  430. {
  431.     const char *fileName;
  432.     const char *const types[2] = {"S3d",
  433.                       NULL};
  434.     id        curWin;
  435.     
  436.     curWin = [self window];
  437.     [saveReq setRequiredFileType:types[0]];
  438.  
  439.     if (curWin == nil) 
  440.     [self showError:"errorOnSaveIn"];
  441.     else {
  442.     // Get a file name from the user; use title of the window as default.
  443.     if (([saveReq runModalForDirectory:"."
  444.                               file:[curWin title]]) &&
  445.           (fileName = [saveReq filename])) 
  446.         [self saveText:curWin inPath:fileName];
  447.     }
  448.     textNeedsSaving = 0;
  449.     return self;
  450. }
  451.  
  452. // saveWindow writes a window out the archive file whose name is specified
  453. // by the second argument. The title of the current window is also set 
  454. // accordingly. 
  455.  
  456. - saveText:(id)win inPath:(const char *)name
  457. {    
  458.     FILE *output;
  459.     char thisline[MAXLINE];
  460.     NXStream    *stream;
  461.     BOOL s_gets();
  462.     
  463.       if((output = fopen(name, "w")) == NULL) {
  464.     sprintf(thisline, "Unable to open file %s", name);
  465.     [self showError:thisline];
  466.     return NULL;
  467.   }
  468.     
  469.     if (delegate && [delegate respondsTo:@selector(stereoView:providePoints:)])
  470.         [delegate stereoView:self providePoints:&stream];
  471.  
  472.       s_gets(thisline, MAXLINE, stream);
  473.       while (!NXAtEOS(stream)) {
  474.         fprintf(output,"%s",thisline);
  475.           s_gets(thisline, MAXLINE, stream);
  476.         }
  477.     fclose(output);
  478.     [win setTitle:name];
  479.     return self;
  480. }
  481.  
  482. // closeRequest closes the current window by simulating a click on the
  483. // closebutton. A check should probably be added to give the user the 
  484. // option of saving the window before closing
  485.  
  486. - closeRequest:sender
  487. {
  488.      if (textNeedsSaving)
  489.         [self saveInRequest:sender];
  490. /*
  491.   [[self window] performClose:sender];
  492. */
  493.     if (delegate && [delegate respondsTo:@selector(stereoView:clearText:)])
  494.         [delegate stereoView:self clearText:fileNAME];
  495.    strcpy(fileNAME,"Untitled");
  496.    [self clear:self];
  497.    [self S_Reset:self];
  498.     textNeedsSaving = 0;
  499.    return self;
  500. }
  501.  
  502. - readSData:(char *)fileName
  503. {
  504.  
  505.     if ([self readFile:fileName] != NULL) {
  506.         [self clear:self];
  507.         [[self window] setTitle:fileName];
  508.         [self textChanged:self];
  509.         [self plot:self];
  510.         }
  511.     return self;
  512. }
  513.  
  514. - drawSelf:(const NXRect *)rects :(int)rectCount
  515. {
  516.     
  517.     NXDrawWhiteBezel(rects,rects);
  518.     [leftView drawSelf:&left_Rect:1];
  519.     [rightView drawSelf:&right_Rect:1];
  520.     return self;
  521. }
  522.  
  523. - readFile:(char *)fileName
  524. {
  525.     FILE *input;
  526.     char thisline[MAXLINE], *cpt, *trimmer;
  527.     
  528.       if((input = fopen(fileName, "r")) == NULL) {
  529.     sprintf(thisline, "Unable to open file %s", fileName);
  530.     [self showError:thisline];
  531.     return NULL;
  532.   }
  533.  
  534.     if (delegate && [delegate respondsTo:@selector(stereoView:clearText:)])
  535.                     [delegate stereoView:self clearText:fileName];
  536.   fgets(thisline, MAXLINE, input);
  537.   while (!feof(input)) {
  538.     trimmer = cpt = &thisline[0];
  539.     while(*trimmer++ != '\n');    trimmer--;
  540.     while(*trimmer-- == ' ');    trimmer++;
  541.     if (*trimmer != '\n') *trimmer++ = '\n'; *trimmer = '\0'; 
  542.     if (delegate && [delegate respondsTo:@selector(stereoView:pointDidChange:)])
  543.                     [delegate stereoView:self pointDidChange:cpt];
  544.   fgets(thisline, MAXLINE, input);
  545.   }
  546.     fclose(input);
  547.     return self;
  548. }
  549.  
  550. static void float_copy (register int n, register float * to, register int
  551.            to_inc, register float * from, register int from_inc)
  552. {
  553.   if (n > 0)
  554.     while (n--) {
  555.       *to = *from;
  556.       to += to_inc;
  557.       from += from_inc;
  558.     }
  559. }
  560.  
  561. BOOL    s_gets(thisline, max, stream)
  562.     char thisline[];
  563.     int max;
  564.     NXStream *stream;
  565. {
  566.     int i;
  567.     
  568.     i = 0;
  569.     while (!NXAtEOS(stream))    {
  570.         thisline[i++] = NXGetc(stream);
  571.         if (i > max) return TRUE;
  572.         if (thisline[i-1] == '\n') {
  573.             thisline[i] = '\0';
  574.             return TRUE;
  575.             }
  576.     }
  577.     return(FALSE);
  578. }
  579.  
  580. - (BOOL) readData:(NXStream *)stream
  581. {
  582.   float *scratch, *fpt;
  583.   char thisline[MAXLINE] ,buffer[MAXLINE], *cpt, c;
  584.   long int count, ncols, nchars, nlines ,tlines, expected_cols;
  585.   datapoints **all_data = toshow, *thesedata;
  586.   float radius;
  587.   float    shade;
  588.   
  589.   long pts_read = 0;
  590.   long num_allocs = 0;
  591.   tlines = 0;
  592.   
  593.   scratch = (float *) calloc(MAXNUM, sizeof(float));
  594.                 /* release existing data */
  595.   while ((thesedata = *all_data) != (datapoints *) NULL) {
  596.     free(thesedata->displayed);
  597.     count = 0;
  598.     while(thesedata->all[count] != (float *) NULL) {
  599.       free(thesedata->all[count]);
  600.       count++;
  601.     }
  602.     free(thesedata->all);
  603.     free(thesedata);
  604.     *all_data++ = (datapoints *) NULL;
  605.   }
  606.   all_data = toshow;        /* read new data */
  607.   s_gets(thisline, MAXLINE, stream);
  608.     tlines++;
  609.   while (!NXAtEOS(stream)) {
  610.       radius = 0.;
  611.     shade = NX_BLACK;
  612.     if(sscanf(thisline, " %1s ", &c) != 1) {
  613.       sprintf(thisline, "Unable to read a specification from text");
  614.       [self showError:thisline];
  615.     return NO;
  616.     }
  617.     switch(c) {
  618.     case 'P': case 'p': 
  619. /*
  620.  *    Scan here for optional arguments such as shade and radius or width
  621.  *    set shade, radius (== width for lines)  pattern?  labels?
  622. */
  623.     sscanf(thisline, "%s %f %f", buffer, &radius, &shade);
  624.     
  625.     case 'N': case 'n': case 'L': case 'l': case 'B':
  626.     case 'b':
  627.       s_gets(thisline, MAXLINE, stream);     /* load a new line */
  628.     tlines++;
  629.       break;
  630.     default:
  631.         /*
  632.       sprintf(thisline, "the beginning of text in line %d doesn't look like a specification",tlines);
  633.       [self showError:thisline];
  634.         return NO;
  635.         */
  636.       c = 'p';
  637.     /* s_gets(thisline, MAXLINE, stream); *//* load a new line */
  638.     tlines++;
  639.     }
  640.     cpt = thisline; fpt = scratch; ncols = 0;
  641.     while(sscanf(cpt, "%f%n", fpt, &nchars) == 1) {
  642.       cpt += nchars;
  643.       if(++ncols > 3) {
  644.           sprintf(buffer,
  645.             "More than 3 columns read in line %d\n%s\n",
  646.             tlines-1, thisline);
  647.         [self showError:buffer];
  648.         return NO;
  649.         }
  650.       if (pts_read++ == MAXNUM)    {
  651.           sprintf(thisline, "Storage space for pts. exceeded");
  652.         [self showError:thisline];
  653.     return NO;
  654.         }
  655.       fpt++;
  656.     }
  657.     if (ncols == 0) {
  658.       sprintf(buffer,
  659.        "Line %d: <%s> does not contain numeric data",tlines,thisline);
  660.       [self showError:buffer];
  661.     return NO;
  662.     }
  663.                 /* digest the rest of the lines */
  664.     expected_cols = ncols; nlines = 1;
  665.     while(s_gets(thisline, MAXLINE, stream)) {
  666.       cpt = thisline; ncols = 0;    tlines++;
  667.       while(sscanf(cpt, "%f %n", fpt, &nchars) == 1) {
  668.     cpt += nchars;
  669.     ncols++;
  670.           if (pts_read++ == MAXNUM)    {
  671.           sprintf(thisline, "Storage space for pts. exceeded");
  672.         [self showError:thisline];
  673.     return NO;
  674.         }
  675.     fpt++;
  676.  
  677.       }
  678.       if (!ncols) break;
  679.       nlines++;
  680.       if (ncols != expected_cols) {
  681.     sprintf(buffer,
  682.         "Expected %d values but got %d values at line %d\nBad Data is:%s",
  683.         expected_cols, ncols, tlines,thisline);
  684.     [self showError:buffer];
  685.     return NO;
  686.       }
  687.     }                /* create the datapoints structure and */
  688.                 /* store the values */
  689.     thesedata = *all_data++ = (datapoints *) malloc(sizeof(datapoints));
  690.     if (++num_allocs > MAXALLOC)    {
  691.         sprintf(thisline, "Number Distinct Groups > MAXALLOC");
  692.         [self showError:thisline];
  693.         return NO;
  694.         }
  695.     switch(c) {
  696.     case 'N': case 'n':
  697.       thesedata->type = NEITHER;
  698.       break;
  699.     case 'L': case 'l':
  700.       thesedata->type = LINES;
  701.       break;
  702.     case 'P': case 'p':
  703.       thesedata->type = POINTS;
  704.       thesedata->radius = radius;
  705.       thesedata->shade = shade;
  706. /*
  707.  *        Store radius, shade, label here in thesedata->radius, etc.
  708. */
  709.       break;
  710.     case 'B': case 'b':
  711.       thesedata->type = BOTH;
  712.       break;
  713.     }
  714.     thesedata->all = (float **)calloc((size_t) expected_cols + 1,
  715.                   (size_t) sizeof(float *));
  716.     for(ncols = 0; ncols < expected_cols; ncols++) {
  717.       thesedata->all[ncols] = (float *)calloc((size_t) nlines, sizeof(float));
  718.       float_copy(nlines, thesedata->all[ncols], 1, scratch + ncols,
  719.          expected_cols);
  720.     }
  721.     thesedata->all[expected_cols] = (float *)NULL;
  722.                 /* make sure path is large enough */
  723.     if (2*nlines >= max_path)
  724.       realloc(path, (2*nlines)*sizeof(float));
  725.     thesedata->npts = nlines;
  726.     memmove(thesedata->displayed = (float **) calloc(3, sizeof(float *)),
  727.         thesedata->all, 3 * sizeof(float *));      
  728.   }
  729.   free(scratch);
  730.   [self display];
  731.   return YES;
  732. }
  733.  
  734. @end
  735.